﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Core.Search
{

    public class SearchDoc
    {
        public Guid DocId { get; set; }
        public Guid BusinessUnitId { get; set; }
        public Guid PluginId { get; set; }
        public string URL { get; set; }
        public string Identifiers { get; set; }
        public string Tags { get; set; }
        public string Title {get; set;}
        public string Keywords {get; set;}
        public string Description {get; set;}
        public string Content {get; set;}
    }

    public class SearchResults
    {
        public string SearchText { get; set; }
        public int PageSize { get; set; }
        public int CurrentPage { get; set; }
        public int Count { get; set; }
        public List<SearchDoc> Results { get; set; }

        public SearchResults()
        {
            Results = new List<SearchDoc>();
        }
    }

    public class SearchEngine
    {
        private Lucene.Net.Analysis.Standard.StandardAnalyzer analyzer;
        private string _indexpath;

        public SearchEngine(Guid DomainId)
        {
            _indexpath = HttpContext.Current.Server.MapPath("~") + "\\App_Data\\Lucene\\" + DomainId.ToString();
            analyzer = new Lucene.Net.Analysis.Standard.StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29);
            // Create the Index if it does not exist and close so that other methods can just append
            lock (HttpContext.Current)
            {
                Lucene.Net.Store.Directory dir = Lucene.Net.Store.FSDirectory.Open(new System.IO.DirectoryInfo(_indexpath));
                Lucene.Net.Index.IndexWriter writer = new Lucene.Net.Index.IndexWriter(dir, analyzer,Lucene.Net.Index.IndexWriter.MaxFieldLength.UNLIMITED);
                writer.Commit();
                writer.Close();
            }
        }

        private static Lucene.Net.Documents.Document CreateDoc(SearchDoc doc)
        {
            Lucene.Net.Documents.Document d = new Lucene.Net.Documents.Document();
            d.Add(new Lucene.Net.Documents.Field("docid", doc.DocId.ToString(), Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.NOT_ANALYZED));
            d.Add(new Lucene.Net.Documents.Field("businessunitid", doc.BusinessUnitId.ToString(), Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.NOT_ANALYZED));
            d.Add(new Lucene.Net.Documents.Field("pluginid", doc.PluginId.ToString(), Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.NOT_ANALYZED));

            Lucene.Net.Documents.Field f;
            f = new Lucene.Net.Documents.Field("content", String.IsNullOrEmpty(doc.Content) ? "" : doc.Content, Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.ANALYZED);
            f.SetBoost(0.5F);
            d.Add(f);
            f = new Lucene.Net.Documents.Field("identifiers", String.IsNullOrEmpty(doc.Identifiers) ? "" : doc.Identifiers, Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.ANALYZED);
            f.SetBoost(2.0F);
            d.Add(f);
            f = new Lucene.Net.Documents.Field("tags", String.IsNullOrEmpty(doc.Tags) ? "" : doc.Tags, Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.ANALYZED);
            f.SetBoost(1.0F);
            d.Add(f);
            f = new Lucene.Net.Documents.Field("url", String.IsNullOrEmpty(doc.URL) ? "" : doc.URL, Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.ANALYZED);
            f.SetBoost(1.2F);
            d.Add(f);
            f = new Lucene.Net.Documents.Field("title", String.IsNullOrEmpty(doc.Title) ? "" : doc.Title, Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.ANALYZED);
            f.SetBoost(1.5F);
            d.Add(f);
            f = new Lucene.Net.Documents.Field("keywords", String.IsNullOrEmpty(doc.Keywords) ? "" : doc.Keywords, Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.ANALYZED);
            f.SetBoost(1.5F);
            d.Add(f);
            f = new Lucene.Net.Documents.Field("description", String.IsNullOrEmpty(doc.Description) ? "" : doc.Description, Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.ANALYZED);
            f.SetBoost(1.5F);
            d.Add(f);
            return d;
        }

        private static SearchDoc GetSearchDoc(Lucene.Net.Documents.Document d)
        {
            SearchDoc doc = new SearchDoc();
            doc.DocId = new Guid(d.Get("docid"));
            doc.BusinessUnitId = new Guid(d.Get("businessunitid"));
            doc.PluginId = new Guid(d.Get("pluginid"));
            doc.URL = d.Get("url");
            doc.Identifiers = d.Get("identifiers");
            doc.Tags = d.Get("tags");
            doc.Title = d.Get("title");
            doc.Keywords = d.Get("keywords");
            doc.Description = d.Get("description");
            doc.Content = d.Get("content");
            return doc;
        }

        public void IndexContent(SearchDoc doc)
        {
            lock (HttpContext.Current)
            {
                Lucene.Net.Store.Directory dir = Lucene.Net.Store.FSDirectory.Open(new System.IO.DirectoryInfo(_indexpath));
                Lucene.Net.Index.IndexWriter modifier = new Lucene.Net.Index.IndexWriter(dir, analyzer, false, Lucene.Net.Index.IndexWriter.MaxFieldLength.UNLIMITED);
                modifier.DeleteDocuments(new Lucene.Net.Index.Term("docid", doc.DocId.ToString()));
                modifier.AddDocument(CreateDoc(doc));
                modifier.Commit();
                modifier.Optimize();
                modifier.Close();
            }
        }

        public void RemoveContent(Guid DocId)
        {
            lock (HttpContext.Current)
            {
                Lucene.Net.Store.Directory dir = Lucene.Net.Store.FSDirectory.Open(new System.IO.DirectoryInfo(_indexpath));
                Lucene.Net.Index.IndexWriter modifier = new Lucene.Net.Index.IndexWriter(dir, analyzer, false, Lucene.Net.Index.IndexWriter.MaxFieldLength.UNLIMITED);
                modifier.DeleteDocuments(new Lucene.Net.Index.Term("docid", DocId.ToString()));
                modifier.Commit();
                modifier.Optimize();
                modifier.Close();
            }
        }

        public SearchResults Search(string SearchText, int PageSize, int PageNo, int MaxPages)
        {
            PageNo = PageNo < 1 ? 1 : PageNo;
            PageSize = PageSize < 11 ? 10 : PageSize;
            MaxPages = MaxPages < 11 ? 10 : MaxPages;
            SearchResults results = new SearchResults();
            List<SearchDoc> docs = results.Results;
            Lucene.Net.QueryParsers.QueryParser parser = new Lucene.Net.QueryParsers.QueryParser(Lucene.Net.Util.Version.LUCENE_29, "content", analyzer);
            Lucene.Net.Search.Query query = parser.Parse(SearchText);
            Lucene.Net.Store.Directory dir = Lucene.Net.Store.FSDirectory.Open(new System.IO.DirectoryInfo(_indexpath));
            int hitsPerPage = PageSize * MaxPages;
            Lucene.Net.Search.IndexSearcher searcher = new Lucene.Net.Search.IndexSearcher(dir, true);
            Lucene.Net.Search.TopScoreDocCollector collector = Lucene.Net.Search.TopScoreDocCollector.create(hitsPerPage, true);
            results.Count = searcher.Search(query).Length();
            searcher.Search(query, collector);
            Lucene.Net.Search.ScoreDoc[] hits = collector.TopDocs().scoreDocs;
            int offset = (PageNo-1) * PageSize;
            int count = Math.Min(hits.Length - offset, PageSize);
            for (int i = 0; i < count; ++i)
            {
                int docId = hits[offset + i].doc;
                Lucene.Net.Documents.Document d = searcher.Doc(docId);
                docs.Add(GetSearchDoc(d));
            }
            searcher.Close();
            dir.Close();
            results.SearchText = SearchText;
            results.CurrentPage = PageNo;
            results.PageSize = PageSize;
            return results;
        }

        public void ReInitialize()
        {
            lock (HttpContext.Current)
            {
                Lucene.Net.Store.Directory dir = Lucene.Net.Store.FSDirectory.Open(new System.IO.DirectoryInfo(_indexpath));
                Lucene.Net.Index.IndexWriter writer = new Lucene.Net.Index.IndexWriter(dir, analyzer, true, Lucene.Net.Index.IndexWriter.MaxFieldLength.UNLIMITED);
                writer.Commit();
                writer.Close();
            }
        }
    }
}